/**
 * 轨道计算核心类
 * 实现六根数到笛卡尔坐标的转换、轨道位置和速度计算、开普勒轨道传播算法
 */
class OrbitCalculator {
	/**
	 * 构造函数
	 * @param {Object} orbitElements - 轨道六根数
	 * @param {number} orbitElements.semiMajorAxis - 半长轴 (km)
	 * @param {number} orbitElements.eccentricity - 偏心率
	 * @param {number} orbitElements.inclination - 轨道倾角 (度)
	 * @param {number} orbitElements.raan - 升交点赤经 (度)
	 * @param {number} orbitElements.argumentOfPerigee - 近地点幅角 (度)
	 * @param {number} orbitElements.meanAnomaly - 平近点角 (度)
	 * @param {number} epoch - 历元时间 (Julian Date)
	 */
	constructor(orbitElements, epoch = null) {
		this.semiMajorAxis = orbitElements.semiMajorAxis; // km
		this.eccentricity = orbitElements.eccentricity;
		this.inclination = this.degToRad(orbitElements.inclination); // 转换为弧度
		this.raan = this.degToRad(orbitElements.raan); // 升交点赤经
		this.argumentOfPerigee = this.degToRad(orbitElements.argumentOfPerigee); // 近地点幅角
		this.meanAnomaly = this.degToRad(orbitElements.meanAnomaly); // 平近点角
		this.epoch = epoch || Date.now(); // 历元时间

		// 地球引力参数 (km³/s²)
		this.GM = 398600.4418;
		// 地球半径 (km)
		this.EARTH_RADIUS = 6371.0;

		// 计算轨道周期和平均运动
		this.orbitalPeriod = this.calculateOrbitalPeriod();
		this.meanMotion = (2 * Math.PI) / this.orbitalPeriod; // rad/s
	}

	/**
	 * 角度转弧度
	 * @param {number} degrees - 角度
	 * @returns {number} 弧度
	 */
	degToRad(degrees) {
		return (degrees * Math.PI) / 180;
	}

	/**
	 * 弧度转角度
	 * @param {number} radians - 弧度
	 * @returns {number} 角度
	 */
	radToDeg(radians) {
		return (radians * 180) / Math.PI;
	}

	/**
	 * 计算轨道周期
	 * @returns {number} 轨道周期 (秒)
	 */
	calculateOrbitalPeriod() {
		return 2 * Math.PI * Math.sqrt(Math.pow(this.semiMajorAxis, 3) / this.GM);
	}

	/**
	 * 求解开普勒方程，计算偏近点角
	 * @param {number} meanAnomaly - 平近点角 (弧度)
	 * @param {number} eccentricity - 偏心率
	 * @param {number} tolerance - 收敛精度
	 * @returns {number} 偏近点角 (弧度)
	 */
	solveKeplerEquation(meanAnomaly, eccentricity, tolerance = 1e-8) {
		let E = meanAnomaly; // 初始猜测值
		let delta = 1;
		let iterations = 0;
		const maxIterations = 100;

		while (Math.abs(delta) > tolerance && iterations < maxIterations) {
			delta = (E - eccentricity * Math.sin(E) - meanAnomaly) / (1 - eccentricity * Math.cos(E));
			E -= delta;
			iterations++;
		}

		if (iterations >= maxIterations) {
			console.warn('开普勒方程求解未收敛');
		}

		return E;
	}

	/**
	 * 计算真近点角
	 * @param {number} eccentricAnomaly - 偏近点角 (弧度)
	 * @param {number} eccentricity - 偏心率
	 * @returns {number} 真近点角 (弧度)
	 */
	calculateTrueAnomaly(eccentricAnomaly, eccentricity) {
		const cosE = Math.cos(eccentricAnomaly);
		const sinE = Math.sin(eccentricAnomaly);

		const cosNu = (cosE - eccentricity) / (1 - eccentricity * cosE);
		const sinNu = (Math.sqrt(1 - eccentricity * eccentricity) * sinE) / (1 - eccentricity * cosE);

		return Math.atan2(sinNu, cosNu);
	}

	/**
	 * 计算轨道半径
	 * @param {number} eccentricAnomaly - 偏近点角 (弧度)
	 * @returns {number} 轨道半径 (km)
	 */
	calculateRadius(eccentricAnomaly) {
		return this.semiMajorAxis * (1 - this.eccentricity * Math.cos(eccentricAnomaly));
	}

	/**
	 * 轨道坐标系到地心惯性坐标系的转换
	 * @param {number} x - 轨道坐标系x坐标
	 * @param {number} y - 轨道坐标系y坐标
	 * @param {number} z - 轨道坐标系z坐标
	 * @returns {Object} 地心惯性坐标系坐标 {x, y, z}
	 */
	orbitToInertial(x, y, z) {
		const cosRaan = Math.cos(this.raan);
		const sinRaan = Math.sin(this.raan);
		const cosInc = Math.cos(this.inclination);
		const sinInc = Math.sin(this.inclination);
		const cosArgPer = Math.cos(this.argumentOfPerigee);
		const sinArgPer = Math.sin(this.argumentOfPerigee);

		// 旋转矩阵元素
		const r11 = cosRaan * cosArgPer - sinRaan * sinArgPer * cosInc;
		const r12 = -cosRaan * sinArgPer - sinRaan * cosArgPer * cosInc;
		const r13 = sinRaan * sinInc;

		const r21 = sinRaan * cosArgPer + cosRaan * sinArgPer * cosInc;
		const r22 = -sinRaan * sinArgPer + cosRaan * cosArgPer * cosInc;
		const r23 = -cosRaan * sinInc;

		const r31 = sinArgPer * sinInc;
		const r32 = cosArgPer * sinInc;
		const r33 = cosInc;

		return {
			x: r11 * x + r12 * y + r13 * z,
			y: r21 * x + r22 * y + r23 * z,
			z: r31 * x + r32 * y + r33 * z
		};
	}

	/**
	 * 计算指定时间的轨道位置和速度
	 * @param {number} time - 时间 (毫秒时间戳或Julian Date)
	 * @returns {Object} 位置和速度 {position: {x, y, z}, velocity: {x, y, z}}
	 */
	calculateStateVector(time) {
		// 计算从历元时间到指定时间的时间差 (秒)
		const deltaTime = (time - this.epoch) / 1000;

		// 计算当前时刻的平近点角
		const currentMeanAnomaly = this.meanAnomaly + this.meanMotion * deltaTime;

		// 求解开普勒方程得到偏近点角
		const eccentricAnomaly = this.solveKeplerEquation(currentMeanAnomaly, this.eccentricity);

		// 计算真近点角
		const trueAnomaly = this.calculateTrueAnomaly(eccentricAnomaly, this.eccentricity);

		// 计算轨道半径
		const radius = this.calculateRadius(eccentricAnomaly);

		// 轨道坐标系中的位置
		const orbitX = radius * Math.cos(trueAnomaly);
		const orbitY = radius * Math.sin(trueAnomaly);
		const orbitZ = 0;

		// 转换到地心惯性坐标系
		const position = this.orbitToInertial(orbitX, orbitY, orbitZ);

		// 计算速度
		const velocity = this.calculateVelocity(eccentricAnomaly, trueAnomaly, radius);

		return {
			position: position,
			velocity: velocity,
			orbitalElements: {
				eccentricAnomaly: this.radToDeg(eccentricAnomaly),
				trueAnomaly: this.radToDeg(trueAnomaly),
				radius: radius
			}
		};
	}

	/**
	 * 计算速度向量
	 * @param {number} eccentricAnomaly - 偏近点角 (弧度)
	 * @param {number} trueAnomaly - 真近点角 (弧度)
	 * @param {number} radius - 轨道半径 (km)
	 * @returns {Object} 速度向量 {x, y, z} (km/s)
	 */
	calculateVelocity(eccentricAnomaly, trueAnomaly, radius) {
		const p = this.semiMajorAxis * (1 - this.eccentricity * this.eccentricity); // 半通径
		const h = Math.sqrt(this.GM * p); // 角动量

		// 轨道坐标系中的速度
		const orbitVx = -(h / radius) * Math.sin(trueAnomaly);
		const orbitVy = (h / radius) * (this.eccentricity + Math.cos(trueAnomaly));
		const orbitVz = 0;

		// 转换到地心惯性坐标系
		return this.orbitToInertial(orbitVx, orbitVy, orbitVz);
	}

	/**
	 * 生成轨道轨迹点
	 * @param {number} startTime - 开始时间 (毫秒时间戳)
	 * @param {number} duration - 持续时间 (秒)
	 * @param {number} stepSize - 时间步长 (秒)
	 * @returns {Array} 轨道点数组 [{time, position, velocity}, ...]
	 */
	generateOrbitTrajectory(startTime, duration, stepSize = 60) {
		const trajectory = [];
		const steps = Math.floor(duration / stepSize);

		for (let i = 0; i <= steps; i++) {
			const time = startTime + i * stepSize * 1000;
			const state = this.calculateStateVector(time);

			trajectory.push({
				time: time,
				position: state.position,
				velocity: state.velocity,
				orbitalElements: state.orbitalElements
			});
		}

		return trajectory;
	}

	/**
	 * 更新轨道参数
	 * @param {Object} newElements - 新的轨道六根数
	 * @param {number} newEpoch - 新的历元时间
	 */
	updateOrbitElements(newElements, newEpoch = null) {
		if (newElements.semiMajorAxis !== undefined) {
			this.semiMajorAxis = newElements.semiMajorAxis;
		}
		if (newElements.eccentricity !== undefined) {
			this.eccentricity = newElements.eccentricity;
		}
		if (newElements.inclination !== undefined) {
			this.inclination = this.degToRad(newElements.inclination);
		}
		if (newElements.raan !== undefined) {
			this.raan = this.degToRad(newElements.raan);
		}
		if (newElements.argumentOfPerigee !== undefined) {
			this.argumentOfPerigee = this.degToRad(newElements.argumentOfPerigee);
		}
		if (newElements.meanAnomaly !== undefined) {
			this.meanAnomaly = this.degToRad(newElements.meanAnomaly);
		}

		if (newEpoch !== null) {
			this.epoch = newEpoch;
		}

		// 重新计算轨道周期和平均运动
		this.orbitalPeriod = this.calculateOrbitalPeriod();
		this.meanMotion = (2 * Math.PI) / this.orbitalPeriod;
	}

	/**
	 * 获取轨道信息
	 * @returns {Object} 轨道信息
	 */
	getOrbitInfo() {
		const perigeeAltitude = this.semiMajorAxis * (1 - this.eccentricity) - this.EARTH_RADIUS;
		const apogeeAltitude = this.semiMajorAxis * (1 + this.eccentricity) - this.EARTH_RADIUS;

		return {
			semiMajorAxis: this.semiMajorAxis,
			eccentricity: this.eccentricity,
			inclination: this.radToDeg(this.inclination),
			raan: this.radToDeg(this.raan),
			argumentOfPerigee: this.radToDeg(this.argumentOfPerigee),
			meanAnomaly: this.radToDeg(this.meanAnomaly),
			orbitalPeriod: this.orbitalPeriod,
			meanMotion: this.meanMotion,
			perigeeAltitude: perigeeAltitude,
			apogeeAltitude: apogeeAltitude,
			orbitType: this.getOrbitType()
		};
	}

	/**
	 * 获取轨道类型
	 * @returns {string} 轨道类型
	 */
	getOrbitType() {
		const altitude = this.semiMajorAxis - this.EARTH_RADIUS;

		if (altitude < 2000) {
			return 'LEO (低地球轨道)';
		} else if (altitude < 35786) {
			return 'MEO (中地球轨道)';
		} else if (Math.abs(altitude - 35786) < 100 && this.eccentricity < 0.01) {
			return 'GEO (地球静止轨道)';
		} else {
			return 'HEO (高地球轨道)';
		}
	}
}

export default OrbitCalculator;
